#pragma once

#include "../reactor/Reactor.hpp"
#include "ClientConnection.hpp"
#include "ServerConnection.hpp"
#include <unordered_map>

namespace network {

    class TcpServer {

    public:

        using recvCallBack = ClientConnection::recvCallBack;

        using closeCallBack = ClientConnection::closeCallBack;

        using acceptCallBack = std::function<void(ClientConnection *)>;

        explicit TcpServer(recvCallBack recv_cb, unsigned int port = 8080,
                           Reactor *reactor = singleton::Singleton<Reactor>::getInstance());

        ~TcpServer();

        /**
          * @brief  启动服务端程序
          * @note   阻塞函数
          */
        void start();

        /**
          * @brief  停止服务端程序
          * @note   反应堆将被停止
          */
        void stop();

        /**
          * @brief  初始化Tcp服务端，进行绑定、监听、设置reactor模型处理器等
          * @note   start函数会调用init，请勿重复调用
          */
        void init();

        /**
          * @brief  设置客户端断开连接时的回调函数
          * @param  cb 回调函数
          */
        void setCloseCallBack(closeCallBack cb) { close_cb_ = std::move(cb); }

        /**
          * @brief  设置客户端建立连接时的回调函数
          * @param  cb 回调函数
          */
        void setAcceptCallBack(acceptCallBack cb) { accept_cb_ = std::move(cb); }

    private:

        // 服务端回调
        void serverHandler(int, EventType, void *);

        // 客户端回调
        void clientHandler(int, EventType, void *);

    private:

        // 服务端连接
        std::shared_ptr<ServerConnection> server_conn_;

        // 连接池（用来保持连接不被析构释放）
        std::unordered_map<int, std::shared_ptr<ClientConnection>> clients_;

        // 客户端收数据回调函数
        recvCallBack recv_cb_;

        // 客户端断开连接回调函数
        closeCallBack close_cb_;

        // 客户端建立连接回调函数
        acceptCallBack accept_cb_;

        // 反应堆指针
        Reactor *reactor_;

    };

    TcpServer::TcpServer(recvCallBack recv_cb, unsigned int port, Reactor *reactor) : recv_cb_(std::move(recv_cb)),
        reactor_(reactor) {
        server_conn_ = std::make_shared<ServerConnection>();
        server_conn_->setPort(port);
    }

    TcpServer::~TcpServer() {
        stop();
    }

    void TcpServer::start() {
        init();
        reactor_->loop();
    }

    void TcpServer::stop() {
        reactor_->stop();
    }

    void TcpServer::init() {
        if (recv_cb_ == nullptr) {
            throw std::runtime_error("the callback functions for receiving messages are not set");
        }
        if (!server_conn_->setPortReuable()) {
            throw std::runtime_error("the port multiplexing setting failed");
        }
        if (!server_conn_->bind()) {
            throw std::runtime_error("failed to bind the port");
        }
        if (!server_conn_->listen()) {
            throw std::runtime_error("listening failed");
        }
        server_conn_->setNonBlock();
        auto server_handler = std::bind(&TcpServer::serverHandler, this, std::placeholders::_1, std::placeholders::_2,
                                        std::placeholders::_3);
        if (!reactor_->registerHandler(server_conn_->getFd(), EventType::Read, server_handler, server_conn_.get())) {
            throw std::runtime_error("Reactor register handler failed");
        }
    }

    void TcpServer::serverHandler(int fd, EventType event, void *context) {
        ServerConnection *server_conn = static_cast<ServerConnection*>(context);
        std::shared_ptr<ClientConnection> client;
        try {
            client = std::make_shared<ClientConnection>(server_conn->accept(), true, reactor_);
            client->setRecvCallBack(recv_cb_);
            if (close_cb_ != nullptr) {
                client->setCloseCallBack(close_cb_);
            }
            client->setNonBlock();
            if (accept_cb_ != nullptr) {
                accept_cb_(client.get());
            }
            clients_.emplace(client->getFd(), client);
            reactor_->registerHandler(client->getFd(), EventType::Read,
                                      std::bind(&TcpServer::clientHandler, this, std::placeholders::_1,
                                                std::placeholders::_2,
                                                std::placeholders::_3), nullptr);
        }
        catch (const std::exception &e) {}
    }

    void TcpServer::clientHandler(int fd, EventType event, void *context) {
        try {
            std::shared_ptr<ClientConnection> client = clients_.at(fd);
            if (event == EventType::Read) {
                client->clientRecv();
            }
            else if (event == EventType::Write) {
                client->clientSend();
            }
            else if (event == EventType::Close) {
                client->clientClose();
                clients_.erase(fd);
            }
        }
        catch (const std::exception &e) {}
    }

}
